package repaso;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

public class Ciudad {
	private String nombre;
	private Set<Lugar> lugares;
	private Map<String, Recorrido> recorridos;

	/**
	 * post: inicializa la instancia de Ciudad con el nombre indicado y sin
	 * 
	 * Lugares asociados.
	 */
	public Ciudad(String nombre) {
		this.nombre = nombre;
		this.lugares = new HashSet<Lugar>();
		this.recorridos = new HashMap<String, Recorrido>();
	}

	/**
	 * post: devuelve el nombre de la Ciudad.
	 */
	public String getNombre() {
		return this.nombre;
	}

	/**
	 * post: agrega nuevoLugar a los Lugares de la Ciudad. No realiza la
	 * operación
	 * 
	 * si ya la Ciudad ya tiene un Lugar con el mismo id.
	 * 
	 * @throws MismaIdException
	 */
	public void agregar(Lugar nuevoLugar) throws MismaIdException {
		Iterator<Lugar> itLugares = this.lugares.iterator();
		while (itLugares.hasNext()) {
			if (itLugares.next().getId() == nuevoLugar.getId()) {
				throw new MismaIdException("Esta id ya existe");
			}
		}
		this.lugares.add(nuevoLugar);
	}

	/**
	 * post: devuelve los Lugares que posee la Ciudad ordenados por nombre.
	 */
	public SortedSet<Lugar> getLugaresPorNombre() {
		SortedSet<Lugar> lugares = new TreeSet<Lugar>(new LugaresComparador());

		Iterator<Lugar> itLugares = this.lugares.iterator();
		while (itLugares.hasNext()) {
			lugares.add(itLugares.next());
		}

		return lugares;
	}

	/**
	 * post: devuelve los Lugares que posee la Ciudad ordenados por id.
	 */
	public SortedSet<Lugar> getLugaresPorId() {
		SortedSet<Lugar> lugares = new TreeSet<Lugar>(
				new IdDeLugaresComparador());

		Iterator<Lugar> itLugares = this.lugares.iterator();
		while (itLugares.hasNext()) {
			lugares.add(itLugares.next());
		}

		return lugares;
	}

	/**
	 * post: devuelve los Lugares que tengan la Etiqueta indicada.
	 */
	public Set<Lugar> getLugaresCon(Etiqueta etiqueta) {
		Set<Lugar> lugaresEtiquetas = new HashSet<Lugar>();
		Iterator<Lugar> itLugares = this.lugares.iterator();
		while (itLugares.hasNext()) {
			Lugar lugar = itLugares.next();
			if (lugar.getEtiquetas().contains(etiqueta)) {
				lugaresEtiquetas.add(lugar);
			}
		}

		return lugaresEtiquetas; // Sin test TODO
	}

	/**
	 * post: devuelve los Lugares que tengan al menos alguna de las Etiquetas
	 * 
	 * indicadas.
	 */
	public Set<Lugar> getLugaresConAlguna(Set<Etiqueta> etiquetas) {
		Set<Lugar> lugaresEtiquetas = new HashSet<Lugar>();
		Iterator<Lugar> itLugares = this.lugares.iterator();
		while (itLugares.hasNext()) {
			Lugar lugar = itLugares.next();
			if (etiquetas.retainAll(lugar.getEtiquetas())) {
				lugaresEtiquetas.add(lugar);
			}
		}

		return lugaresEtiquetas; // Sin test TODO

	}

	/**
	 * post: devuelve los Lugares que estén localizados en la región delimitada
	 * por
	 * 
	 * los valores dados.
	 */
	public Set<Lugar> getLugaresEnRegion(double latitudMaxima,
			double latitudMinima, double longitudMaxima, double longitudMinima) {

		Set<Lugar> lugaresEnRegion = new HashSet<Lugar>();
		Iterator<Lugar> itLugares = this.lugares.iterator();
		while (itLugares.hasNext()) {
			Lugar lugar = itLugares.next();
			if (lugar.getLatitud() > latitudMinima
					&& lugar.getLatitud() < latitudMaxima
					&& lugar.getLongitud() > longitudMinima
					&& lugar.getLongitud() < longitudMaxima) {
				lugaresEnRegion.add(lugar);
			}
		}

		return lugaresEnRegion; // Sin test TODO
	}

	/**
	 * post: devuelve un Mapa en el que se asocia para cada Etiqueta la cantidad
	 * de Lugares de la Ciudad que la poseen.
	 */
	public Map<Etiqueta, Integer> contarLugaresPorEtiqueta() {
		Map<Etiqueta, Integer> lugares = new HashMap<Etiqueta, Integer>();

		Iterator<Lugar> itLugares = this.lugares.iterator();
		while (itLugares.hasNext()) {
			Lugar lugar = itLugares.next();
			Iterator<Etiqueta> itEtiqueta = lugar.getEtiquetas().iterator();
			while (itEtiqueta.hasNext()) {
				Etiqueta etiquetaActual = itEtiqueta.next();
				if (lugares.containsKey(etiquetaActual)) {
					int contador = lugares.get(etiquetaActual) + 1;
					lugares.put(etiquetaActual, contador);
				} else {
					lugares.put(etiquetaActual, 1);
				}
			}

		}
		return lugares; // Sin Test TODO
	}

	/**
	 * post: devuelve un Mapa en el que se asocia para cada Etiqueta la lista
	 * 
	 * de Lugares que poseen esta etiqueta. Si un Lugar posee más de una
	 * 
	 * Etiqueta estará en más de una asociación.
	 */
	public Map<Etiqueta, List<Lugar>> getLugaresPorEtiqueta() {
		Map<Etiqueta, List<Lugar>> lugares = new HashMap<Etiqueta, List<Lugar>>();

		Iterator<Lugar> itLugares = this.lugares.iterator();
		while (itLugares.hasNext()) {
			Lugar lugar = itLugares.next();
			Iterator<Etiqueta> itEtiqueta = lugar.getEtiquetas().iterator();
			while (itEtiqueta.hasNext()) {
				Etiqueta etiquetaActual = itEtiqueta.next();
				if (lugares.containsKey(etiquetaActual)) {
					List<Lugar> lugarConEtiqueta = lugares.get(etiquetaActual);
					lugarConEtiqueta.add(lugar);
					lugares.put(etiquetaActual, lugarConEtiqueta);
				} else {
					lugares.put(etiquetaActual, new LinkedList<Lugar>());
				}
			}

		}
		return lugares; // Sin Test TODO

	}

	/**
	 * pre : idLugares contiene los valores de id de Lugares que pertenecen a la
	 * Ciudad. post: crea un nuevo Recorrido con el nombre indicado, agregándole
	 * los Lugares indicados a partir de la Lista idLugares, y lo agrega a la
	 * Ciudad.
	 */
	public void agregarRecorrido(String nombre, List<Integer> idLugares) {
		Recorrido nuevoRecorrido = new Recorrido(nombre);
		Iterator<Lugar> itLugares = this.lugares.iterator();
		while (itLugares.hasNext()) {
			Lugar lugarIterado = itLugares.next();
			Iterator<Integer> itIdLugares = idLugares.iterator();
			while (itIdLugares.hasNext()) {
				int idDeLugarIterado = itIdLugares.next();
				if (lugarIterado.getId() == idDeLugarIterado) {
					nuevoRecorrido.addLugar(lugarIterado);
				}
			}
		}
		this.recorridos.put(nombre, nuevoRecorrido);
	}

	/**
	 * post: devuelve el Recorrido identificado por nombre que posee la Ciudad o
	 * 
	 * null si no existe.
	 */
	public Recorrido getRecorrido(String nombre) {
		if (this.recorridos.containsKey(nombre)) {
			return this.recorridos.get(nombre);
		} else {
			return null;
		}
	}
}
